package com.icinfo.cloud.provider.punish.common.seal.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.icinfo.cloud.common.security.util.UserUtil;
import com.icinfo.cloud.provider.common.constant.Constant;
import com.icinfo.cloud.provider.common.enums.AttachmentRequestContentTypeEnum;
import com.icinfo.cloud.provider.common.enums.FileTransferFormEnum;
import com.icinfo.cloud.provider.common.enums.YesOrNoEnum;
import com.icinfo.cloud.provider.common.exception.BusinessException;
import com.icinfo.cloud.provider.common.utils.Base64ToMultipartFileUtil;
import com.icinfo.cloud.provider.common.utils.FileUtil;
import com.icinfo.cloud.provider.common.utils.SpringContextUtil;
import com.icinfo.cloud.provider.punish.common.caseregister.service.IRegisterCommonService;
import com.icinfo.cloud.provider.punish.common.seal.dto.FileSealInfoQueryDto;
import com.icinfo.cloud.provider.punish.common.seal.service.IFileSealService;
import com.icinfo.cloud.provider.punish.common.seal.vo.ConfigOrgRelatedSealVo;
import com.icinfo.cloud.provider.punish.config.service.IConfigOrgRelatedSealService;
import com.icinfo.cloud.xzcf.provider.member.org.dto.Org;
import com.icinfo.cloud.xzcf.provider.member.userinfo.dto.UserDTO;
import com.timevale.esign.result.account.LoginResult;
import com.timevale.esign.result.file.SignPDFResult;
import com.timevale.esign.sdk.account.AccountService;
import com.timevale.esign.sdk.account.AccountServiceImpl;
import com.timevale.esign.sdk.file.LocalFileService;
import com.timevale.esign.sdk.file.LocalFileServiceImpl;
import com.timevale.tgtext.text.pdf.PdfReader;
import esign.bean.PosBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描述：文件签章接口实现类
 *
 * @author xiangyuyu
 * @date 2022/3/1 20:13
 */
@Service
@Slf4j
public class FileSealServiceImpl implements IFileSealService {
    @Resource
    private IConfigOrgRelatedSealService configOrgRelatedSealService;
    @Resource
    private IRegisterCommonService commonService;

    @Value("${document.url}")
    private String url;

    @Value("${document.api.uploadBase64File}")
    private String uploadBase64File;

    /**
     * 【V1.0.0】描述：签章并获取签章后的文件
     *
     * @param queryDto {@link FileSealInfoQueryDto}
     * @return Map<String, Object> 签章结果信息("errorMsg"-错误信息【String】/"fileBytes"-签章完成的文件字节数组【byte[]】)
     * @author xiangyuyu
     * @date 2022-03-02 09:09
     */
    @Override
    public Map<String, Object> getFileAfterSeal(FileSealInfoQueryDto queryDto) {
        LocalDateTime startTime = LocalDateTime.now();
        log.info("getFileAfterSeal start...startTime:{}", startTime);
        //获取机构统一社会信用代码
        UserDTO userInfo = UserUtil.getUserInfo();
        if (ObjectUtils.isEmpty(userInfo)) {
            throw new BusinessException("登录失效");
        }
        Org orgInfo = commonService.getUserOrgMsg();
        String uniscid = orgInfo.getUniCode();
        String uniscidInput = queryDto.getUniscid();
        if(!ObjectUtils.isEmpty(uniscidInput)){
            log.info("use user input uniscidInput:"+uniscidInput);
            uniscid = uniscidInput;
        }
        String sealCode = "33100000077416";
        if(YesOrNoEnum.YES.getCode().equals(queryDto.getIsSealPreview())){
            if(!ObjectUtils.isEmpty(queryDto.getSealCode())){
                sealCode = queryDto.getSealCode();
            } else {
                throw new BusinessException("印章预览时请指定印章编码");
            }
        } else {
            if (SpringContextUtil.checkIsPrdProfile()) {
                if (ObjectUtils.isEmpty(uniscid)) {
                    throw new BusinessException("无统一社会信用代码");
                } else {
                    ConfigOrgRelatedSealVo configOrgRelatedSeal = configOrgRelatedSealService.getOrgRelatedSealConfigInfo(uniscid,"");
                    sealCode = configOrgRelatedSeal.getSealCode();
                    if (ObjectUtils.isEmpty(configOrgRelatedSeal) || ObjectUtils.isEmpty(sealCode)) {
                        throw new BusinessException("未匹配到印章编码信息，请联系部门管理员确认“单位参数配置“中印章编码是否正确。");
                    }
                }
            }
        }
        List<File> waitToDelFiles = new ArrayList<>();
        Map<String, Object> resultMap = new HashMap<>(2);
        //参数校验
        String errorMsg = fileSealInfoValidated(queryDto);
        if (!ObjectUtils.isEmpty(errorMsg)) {
            log.error(errorMsg);
            resultMap.put("errorMsg", errorMsg);
            resultMap.put("fileBytes", null);
            return resultMap;
        }
        Map<String, Object> posMap = assemblePosBean(queryDto);
        PosBean posBean = (PosBean) posMap.get("posBean");
        int signType = (int) posMap.get("signType");
        SignPDFResult signPdfResult;
        byte[] desFileBuffer = null;
        String dir = "pdfFiles";
        try {
            //走对应的签章渠道
            MultipartFile multipartFile;
            if (FileTransferFormEnum.FILE_TRANSFER_FORM_1.getCode().equalsIgnoreCase(queryDto.getFileTransferForm())) {
                String base64string = queryDto.getFileBase64();
                multipartFile = new Base64ToMultipartFileUtil(base64string, "data:application/pdf;base64,");
            } else {
                multipartFile = queryDto.getFile();
            }
            File oriFile = null;
            if (!"".equals(multipartFile) && multipartFile.getSize() > 0) {
                InputStream ins = multipartFile.getInputStream();
                oriFile = new File(multipartFile.getOriginalFilename());
                FileUtil.inputStreamToFile(ins, oriFile);
                ins.close();
                String srcFile = oriFile.getAbsolutePath();
                log.info("srcFile:" + srcFile);
                //pdf临时生成目录
                File fileMkd = new File(dir);
                if (!fileMkd.exists()) {
                    fileMkd.mkdirs();
                }
            }
            String fileName = oriFile.getName();
            String desFilePath = dir + File.separator + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + fileName;
            LocalFileService localfileservice = new LocalFileServiceImpl();
            log.info("localfileservice new end time:{}", LocalDateTime.now());
            String accountId = doAppLogin(sealCode);
            LocalDateTime startTimeSign = LocalDateTime.now();
            log.info("localSignPDF start...startTimeSign:{}", startTimeSign);
            log.info("localSignPDF param-accountId:{},orgCode:{}", accountId, orgInfo.getOrgCode());
            signPdfResult = localfileservice.localSignPDF(accountId, null, oriFile.getAbsolutePath(), desFilePath, 0,
                    signType, posBean);
            LocalDateTime endTimeSign = LocalDateTime.now();
            Duration durationSign = Duration.between(startTime, endTimeSign);
            log.info("localSignPDF end endTimeSign::{},timeConsume:{}", endTimeSign, durationSign.toMillis());
            log.info("pdf:" + signPdfResult);
            if (ObjectUtils.isEmpty(signPdfResult)) {
                errorMsg = "签章失败-signPDFResult：null";
                log.warn(errorMsg);
            } else {
                int errCode = signPdfResult.getErrCode();
                if (!ObjectUtils.isEmpty(errCode) && !Constant.INTEGER_0.equals(errCode)) {
                    //errorMsg = "签章失败-signPDFResult：error,errorCode:" + signPdfResult.getErrCode() + "，msg:" + signPdfResult.getMsg();
                    if(4023==signPdfResult.getErrCode()){
                        errorMsg = "印章过期或已失效";
                    }else {
                        errorMsg = "印章未授权";
                    }
                    log.warn(errorMsg);
                }
            }
            FileInputStream fis = new FileInputStream(desFilePath);
            desFileBuffer = FileUtil.inputStreamToByte(fis);
            waitToDelFiles.add(oriFile);
            waitToDelFiles.add(new File(desFilePath));
            delRedundancyFile(waitToDelFiles);
            resultMap.put("fileBytes", desFileBuffer);
            resultMap.put("errorMsg", errorMsg);
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
            errorMsg = "请输入完整的base64信息";
            log.warn(errorMsg);
            resultMap.put("fileBytes", desFileBuffer);
            resultMap.put("errorMsg", errorMsg);
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            errorMsg = ObjectUtils.isEmpty(errorMsg) ? "签章失败：" + e.getMessage() : errorMsg;
            log.warn(errorMsg);
            resultMap.put("fileBytes", desFileBuffer);
            resultMap.put("errorMsg", errorMsg);
            return resultMap;
        }
        LocalDateTime endTime = LocalDateTime.now();
        Duration duration = Duration.between(startTime, endTime);
        log.info("getFileAfterSeal end endTime::{},timeConsume:{}", endTime, duration.toMillis());
        return resultMap;
    }

    @Override
    public String dealFileSeal(FileSealInfoQueryDto queryDto) {
        queryDto.setFileType("pdf");
        queryDto.setFileTransferForm("1");
        queryDto.setFileBase64(queryDto.getFileBase64());
        queryDto.setAddSignTime(false);
        queryDto.setSealCode("");
        queryDto.setKey("签章处");
        queryDto.setSignType(4);
        queryDto.setSealCode("12");
        queryDto.setPosX(0);
        queryDto.setPosY(-15);
        //签章
        Map<String, Object> fileAfterSeal = getFileAfterSeal(queryDto);
        if (ObjectUtil.isNotEmpty(fileAfterSeal.get("errorMsg"))) {
            log.info("warn fileSealService,result:{}", fileAfterSeal.get("errorMsg"));
            throw new BusinessException(fileAfterSeal.get("errorMsg").toString());
        }
        //获取pdf页码若2张以上，加盖骑缝章
        try {
            byte[] fileBytes = (byte[]) fileAfterSeal.get("fileBytes");
            InputStream ins = new ByteArrayInputStream(fileBytes);
            PdfReader pdfReader = new PdfReader(ins);
            int pages = pdfReader.getNumberOfPages();
            if (pages > 1) {
                queryDto.setFileType("pdf");
                queryDto.setFileTransferForm("1");
                String base64 = Base64.encodeBase64String(fileBytes);
                queryDto.setFileBase64(base64);
                queryDto.setKey("");
                queryDto.setSealCode("21");
                queryDto.setSignType(3);
                queryDto.setPosX(100);
                queryDto.setPosY(450);
                queryDto.setPosPage("1-" + pages);
                fileAfterSeal = getFileAfterSeal(queryDto);
                if (ObjectUtil.isNotEmpty(fileAfterSeal.get("errorMsg"))) {
                    log.info("warn fileSealService,result:{}", fileAfterSeal.get("errorMsg"));
                    throw new BusinessException(fileAfterSeal.get("errorMsg").toString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new BusinessException("系统异常:" + e.getMessage());
        }
        //上传签章后的文件到oss
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fileBytes", fileAfterSeal.get("fileBytes"));
        jsonObject.put("fileType", "pdf");
        jsonObject.put("fileName", "");
        JSONObject parseObject = JSON.parseObject(HttpUtil.post(url + uploadBase64File, jsonObject.toString()));
        String cd = "code";
        int th = 200;
        if (parseObject.getInteger(cd) == th) {
            String url = parseObject.getJSONObject("data").getString("ossUrl");
            log.info("签章后地址：{}", url);
            return url;
        } else {
            log.info("warn receive sign file upload oss fail,result:{}", parseObject.toString());
            throw new BusinessException("签章后上传文件失败：" + jsonObject.getString("message"));
        }
    }

    /**
     * 描述：删除冗余文件
     *
     * @param redundancyFiles 冗余文件
     * @author xiangyuyu
     * @date 2021-11-24 20:04
     */
    private void delRedundancyFile(List<File> redundancyFiles) {
        redundancyFiles.forEach(e -> {
            if (e.exists()) {
                e.delete();
            }
        });
    }

    /**
     * 描述：组装签章请求入参
     *
     * @param queryDto 文件签章信息查询数据传输对象
     * @return Map<String, Object> 签章请求入参
     * @author xiangyuyu
     * @date 2021-11-12 17:30
     */
    private Map<String, Object> assemblePosBean(FileSealInfoQueryDto queryDto) {
        Map<String, Object> map = new HashMap<>(2);
        PosBean posBean = new PosBean();
        // 签署类型 1-单页签章，2-多页，3-骑缝，4-关键字
        int signType = 1;
        if (!ObjectUtils.isEmpty(queryDto.getSignType())) {
            signType = queryDto.getSignType();
        }
        if (Constant.INTEGER_4 == signType) {
            // 定义盖章类型:1-关键字盖章
            posBean.setPosType("1");
            // 关键字，如果设置了坐标盖章，不要这个值
            posBean.setKey("浙江省自然资源");
            // 签署位置X坐标,关键字时以关键字为原点,控制左右移动距离
//            posBean.setPosX(0);
            // 签署位置Y坐标,关键字时以关键字为原点,控制上下移动距离
//            posBean.setPosY(0);
        }

        if (Constant.INTEGER_1 == signType) {
            // 定义盖章类型:0-坐标盖章；
            posBean.setPosType("0");
            // 签署页码
            posBean.setPosPage("1");
            // 签署位置X坐标,以pdf页面的左下角作为原点,控制左右移动距离
            posBean.setPosX(200);
            // 签署位置Y坐标,以pdf页面的左下角作为原点,控制上下移动距离
            posBean.setPosY(180);
        }
        if (Constant.INTEGER_3 == signType) {
            // 定义盖章类型:0-坐标盖章；
            posBean.setPosType("0");
            // 签署页码
            posBean.setPosPage(queryDto.getPosPage());
            // 签署位置X坐标,以pdf页面的左下角作为原点,控制左右移动距离
            posBean.setPosX(queryDto.getPosX());
            // 签署位置Y坐标,以pdf页面的左下角作为原点,控制上下移动距离
            posBean.setPosY(queryDto.getPosY());
        }
        posBean.setPosX(ObjectUtils.isEmpty(queryDto.getPosX()) ? 0 : queryDto.getPosX());
        posBean.setPosY(ObjectUtils.isEmpty(queryDto.getPosY()) ? 0 : queryDto.getPosY());
        //    Integer signType = ObjectUtils.isEmpty(queryDto.getSignType()) ? Constant.INTEGER_1 : queryDto.getSignType();
        String key = queryDto.getKey();
        if (!ObjectUtils.isEmpty(key)) {
            posBean.setPosPage("");
            signType = Constant.INTEGER_4;
        } else {
            posBean.setPosPage(ObjectUtils.isEmpty(queryDto.getPosPage()) ? Constant.STRING_1 : queryDto.getPosPage());
        }
        posBean.setKey(key);
        /*
        posBean.setKeyIndex(ObjectUtils.isEmpty(queryDto.getKeyIndex()) ? 0 : queryDto.getKeyIndex());
        posBean.setAddSignTime(ObjectUtils.isEmpty(queryDto.getAddSealTime()) ? false : queryDto.getAddSealTime());*/
        map.put("signType", signType);
        map.put("posBean", posBean);
        return map;
    }


    /**
     * 描述：文件签章信息查询数据传输对象数据合法性校验【目前仅支持指定pdf文件签章】
     *
     * @param queryDto 文件签章信息查询数据传输对象
     * @return String 错误信息
     * @author XiangYuYu
     * @date 2021/11/09
     */
    private String fileSealInfoValidated(FileSealInfoQueryDto queryDto) {
        String errorMsg = "";
        try {
            String sealCode = queryDto.getSealCode();
            if (ObjectUtils.isEmpty(sealCode)) {
                errorMsg = "请指定印章编码";
                return errorMsg;
            }
            String fileType = queryDto.getFileType();
            String fileTransferForm = queryDto.getFileTransferForm();
            if (!FileTransferFormEnum.isLegalEnumCode(fileTransferForm)) {
                errorMsg = "请指定签章文件传输形式（1-base64/2-文件对象）";
                return errorMsg;
            } else {
                if (FileTransferFormEnum.FILE_TRANSFER_FORM_1.getCode().equalsIgnoreCase(fileTransferForm)
                        && ObjectUtils.isEmpty(queryDto.getFileBase64())) {
                    errorMsg = "请指定需要签章的base64信息";
                    return errorMsg;
                }
                MultipartFile file = queryDto.getFile();
                if (FileTransferFormEnum.FILE_TRANSFER_FORM_2.getCode().equalsIgnoreCase(fileTransferForm)) {
                    if (ObjectUtils.isEmpty(file)) {
                        errorMsg = "请指定需要签章的文件";
                        return errorMsg;
                    }
                    if (ObjectUtils.isEmpty(fileType)) {
                        errorMsg = "请指定需要签章的文件类型";
                        return errorMsg;
                    }
                    if (AttachmentRequestContentTypeEnum.pdf.getCode().equalsIgnoreCase(fileType)) {
                        String fileFullName = file.getOriginalFilename();
                        log.info("fileFullName:" + fileFullName);
                        String suffix = fileFullName.substring(fileFullName.lastIndexOf(Constant.STRING_POINT) + 1);
                        if (!AttachmentRequestContentTypeEnum.pdf.getCode().equalsIgnoreCase(suffix)) {
                            errorMsg = "文件后缀名与文件类型不匹配(" + "fileType:" + fileType + ",文件后缀名：" + suffix + ")";
                            return errorMsg;
                        }
                    } else {
                        errorMsg = "签章的文件类型必须为pdf";
                        return errorMsg;
                    }
                }
            }
        } catch (Exception e) {
            errorMsg = "文件签章信息查询数据传输对象数据合法性校验时出现异常：" + e.getMessage();
            log.error(errorMsg);
        }
        return errorMsg;
    }


    /***
     * 第三方应用登录（用户登录）
     *
     * @param sealCode
     *            老的电子印章系统的appkey（云证书序列号），新印章的印章编码
     * @return
     */
    public static String doAppLogin(String sealCode) {
        // 请每个接口的调用都new一个新的对象,不要多次签署使用同一个对象进行调用。
        AccountService accountService = new AccountServiceImpl();
        // 第三方应用登录实体类，请每个接口的调用都new一个新的对象,不要多次签署使用同一个对象进行调用。
        LoginResult loginResult = accountService.appLogin(sealCode);
        // 用户编号
        String accountId = null;
        if (0 != loginResult.getErrCode()) {
            String errorMsg = "签章鉴权：errCode = " + loginResult.getErrCode()
                    + ", Msg = " + loginResult.getMsg();
            throw new BusinessException(errorMsg);
        } else {
            accountId = loginResult.getAccountId();
            System.out.println("用户登录成功：accountId = " + accountId);
        }
        return accountId;
    }


}
